package ws.jonas.common.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException;

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import ws.jonas.common.config.Config;
import ws.jonas.common.log.Logger;
import ws.jonas.common.model.Product;
import ws.jonas.common.model.Store;

public class WebQuerier {
	private String url;
	private int prodnr;

	private static final Logger log = Logger.getInstance(WebQuerier.class);

	public WebQuerier(String prodnr) throws NumberFormatException {
		this.prodnr = Integer.parseInt(prodnr);
		url = String.format(Config.get(Config.QUERY_URL_KEY), prodnr);
	}

	public Product query() {
		log.info("Querying prodnr " + prodnr);
		Connection conn = null;
		try {
			conn = Jsoup.connect(url);
		} catch (IllegalArgumentException e) {
			log.severe("Bad URL: " + e);
			return null;
		} 
		try {
			conn.timeout(Integer.parseInt(Config.get(Config.CONNECTION_TIMEOUT_MS_KEY)));
		} catch (NumberFormatException e) {
			log.warning("Unable to parse connection timeout, taking default value");
			conn.timeout(Integer.parseInt(Config.getDefault(Config.CONNECTION_TIMEOUT_MS_KEY)));
		}

		int retries;
		try {
			retries = Integer.parseInt(Config.get(Config.CONNECTION_RETRIES_KEY));
		} catch (NumberFormatException e) {
			log.warning("Failed to parse connection retries, taking default value");
			retries = Integer.parseInt(Config.getDefault(Config.CONNECTION_RETRIES_KEY));
		}
		
		Document doc = null;
		for (int i = 1; i <= retries; ++i) {
			try {
				doc = conn.get();
				break;
			} catch (IOException e) {
				log.info("Try #" + i + " failed: " + url.toString());
			}
		}

		if (doc == null) {
			log.warning("Failed to retrieve: " + url.toString());
			return null;
		}
		
		Product product = new Product(prodnr);
		
		//Get name
		Elements elements = doc.select("div.beverageContent div.beverageProperties h1");
		String name = elements.text();
		product.setName(name);
		
		try {
			//Get volume
			elements = doc.select("div.beverageListInfo table.beverageListTable td.attributename, div.beverageListInfo table.beverageListTable td.attributevalue");
			ArrayList<String> volumes = new ArrayList<String>();
			for (Iterator<Element> iterator = elements.iterator(); iterator.hasNext(); ) {
				Element element = iterator.next();
				if (element.text().equals("Förpackning")) {
					String volume = iterator.next().text().trim();
					volumes.add(volume);
				}
			}
			
			//Get county, city, address, volume(s)
			elements = doc.select("div#byMap fieldset.store legend span, div#byMap fieldset.store strong, div#byMap fieldset.store span.storeLocation, div#byMap fieldset.store td.amount");
			
			for (ListIterator<Element> iterator = elements.listIterator(); iterator.hasNext(); ) {
				String county = iterator.next().text().trim();
				while (iterator.hasNext()) {
					Element element = iterator.next();
					if (element.nodeName().equals("span") && !element.className().equals("storeLocation")) {
						//New county found, break
						iterator.previous();
						break;
					}
					String city = element.text();
					String address = iterator.next().text();
					Store store = new Store(address, city, county);
					product.addStore(store);
					
					for (String volume : volumes) {
						int quantity;
						String text = iterator.next().text().trim();
						try {
							quantity = Integer.parseInt(text.substring(0, text.indexOf(" ")));
						} catch (NumberFormatException e) {
							log.warning("Unable to parse stock quantity: " + text + ". Skipping");
							log.warning(e);
							continue;
						}
						
						store.addQuantity(volume, quantity);
					}
				}
			}
		} catch (NoSuchElementException e) {
			log.warning("Web page structured unexpectedly.");
			log.warning(e);
			return null;
		}
		log.info("Query for prodnr " + prodnr + " done");
		return product;
	}
}
